OpenID Connectのstate・nonce・PKCEについて使用法も含めて説明してみる
prismatix事業部の宮部です。
今日はクラスメソッド設立記念日かつ、七夕なので「織姫と彦星が天の川挟んでauth danceやってみた」みたいな記事書こうと思ったんですが自分で何書いてるかわからなくなったのでもう少し実用的な話を考えてみました。
OAuth2.0やOpenID Connectでは各種の攻撃の対策として使用するクエリパラメータが存在します。
ただし、これらのパラメータが付与されているかのハンドリングは簡単ですが、実際にそれをIdentity Provider(認証システム)やRelying Party(利用システム)が正しく検証しているかの保証はむずかしかったりします。
SDKやライブラリにこの辺の処理は任せてるというケースも多いでしょうが、「検証の方法は互いに提供しているのでしっかり検証しているはず」という信頼関係が必要になってくるので啓蒙の意味も込めて各パラメータの用途などについて書いていこうと思います。
前提条件
今回は state
, nonce
, PKCE
の説明をするために認可コードフローが前提です。
ざっくりとした図になりますが、今回はこの中の6, 8, 10番の検証の部分について説明していきます。
state
state
はCSRFを利用して、他者に自身の認可コードを使って処理させるような行為を防ぐことができます。
例えば、4番で取得した認可コード付きのURLを気づかないうちに他者に踏ませてしまえば被害者が個人情報を入力したり、サービスを利用した後で攻撃者が普通にログインしてしまえばその情報を参照することができるでしょう。
この対策として、3番で送る認可リクエストにユーザのセッションに紐づく識別子であるstateを付与すると、4番で認可コードと一緒に先ほど付与したstate
が返却されるのでそのstateの値が同一のものかを検証しましょう(6番)
当然、返却されてきたstate
を検証しなければ意味がないですし、毎回同じだったり推測可能な値を付与していたとしても同じく悪用されてしまいます。
nonce
nonce
はなんらかの方法で取得した他者のIdトークンを使って自身の認可コードを使ってRelying Partyで認証しようとするリプレイアタックを防ぐことができます。
Idトークン自体はIdentity Providerが発行した正式なものであるため、Relying Partyはそれを受け入れてしまい、被害者のアカウントとして認証してしまうでしょう。
この対策として、3番で送る認可リクエストにstate
と同じくユーザのセッションに紐づく識別子であるnonce
を付与すると、9番で受け取ったIdトークンのclaimの中にnonce
が存在するのでそのnonce
の値が同一のものかを検証しましょう(10番)
こちらもstate
と同様に、返却されてきたnonce
を検証しなければ意味がないですし、毎回同じだったり推測可能な値を付与していたとしても同じく悪用されてしまいます。
PKCE
PKCEはProof Key for Code Exchangeの略称であり、PKCEというパラメータが存在するわけではありません。
PKCEは、認可コードを横取りして別のサービスが利用するのを防ぐことができます。
例えばカスタムURIスキームを使用している場合、悪意のあるサービスがそのカスタムURIスキームを乗っ取っていると4番で認可コードを持ってIdentity Providerからリダイレクトしてきた際に、悪意のあるサービスがその認可コードを使ってIdトークンやアクセストークン、リフレッシュトークンを取得できてしまいます。
この対策であるPKCEでは以下の3つのパラメータが登場します。
- code_verifier
- code_challenge_method
- code_challenge
簡単に説明すると、code_verifier
をcode_challenge_method
の方式で変換したものがcode_challenge
です。
code_challenge_method
にはplain
とS256
の2種類が存在し、plain
の場合はcode_verifier
とcode_challenge
の値は同じになり、S256
の場合はcode_verifier
をSHA-256でハッシュ化したものがcode_challenge
の値が同じになります。
まず、3番で送る認可リクエストにcode_challenge_method
とcode_challenge
を付与します。その後、7番のトークンリクエストで認可コードと一緒にcode_verifier
を渡すと、Identity Providerは先ほどの認可リクエスト時に受け取ったcode_challenge_method
で変換したcode_verifier
がcode_challenge
と同じかを検証します(8番)
こちらは、Identity Providerが検証をしなければ意味がないですし、Relying Partyが毎回同じ値や推測可能な値を付与していたとしても悪用されてしまいます。
まとめ
state
はcsrf対策nonce
はリプレイアタック対策PKCE
は認可コードの横取り対策
そして、全部同じ値ではなくちゃんと推測不可能な値を使用し、ちゃんと検証もしましょう。
参考資料
- Final: OpenID Connect Core 1.0 incorporating errata set 1
-
OAuth 2.0 / OpenID Connectにおけるstate, nonce, PKCEの限界を意識する - r-weblife
最後に
prismtatixでは、認証・認可・ユーザー管理基盤開発エンジニアを募集しています。
昨今のサービスには必要不可欠な認証認可について一緒に考え、プロダクトを育ててくれる方を探しています。
少しでもご興味をお持ちいただけましたら、ぜひご応募ください。 お待ちしています。